#include "KeyboardHook.h"
#include "Log.h"
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Diagnostics;
namespace GTA
{
	bool KeyboardHook::AttachHook(bool isglobal,IntPtr hmodule, System::UInt32 threadid)
	{
		if (isHooked) return true;
		if (isglobal)
		{
			Log::Debug("Attaching Global Keyboard hook. ("+hmodule+", 0)");
			KeyboardHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, _procKeyboard, hmodule, 0); 
			MouseHookHandle = SetWindowsHookEx(WH_MOUSE_LL, _procMouse, hmodule, 0); 
			isHooked=true;
			if ((int)KeyboardHookHandle <= 255) {isHooked=false; Log::Debug("Global Keyboard Hook Attach failed!: "+KeyboardHookHandle.ToString());}
			if ((int)MouseHookHandle <= 255) {isHooked=false; Log::Debug("Global Mouse Hook Attach failed!: "+MouseHookHandle.ToString());}
			if (isHooked) Log::Debug("Attach Global Keyboard Hook Success.");
		}
		else
		{
			String^ modname = Process::GetCurrentProcess()->MainModule->ModuleName;
			Log::Debug("Attaching App Keyboard hook to "+modname+". ("+hmodule+", "+threadid+")");
			KeyboardHookHandle = SetWindowsHookEx(WH_KEYBOARD, _procKeyboard, hmodule, threadid); 
			MouseHookHandle = SetWindowsHookEx(WH_MOUSE_LL, _procMouse, hmodule, 0);  // argh, it dont work if not global. bad. todo: non global mouse hook
			isHooked=true;
			if ((int)KeyboardHookHandle <= 255) {isHooked=false; Log::Debug("App Keyboard Hook Attach failed!: "+KeyboardHookHandle.ToString());}
			if ((int)MouseHookHandle <= 255) {isHooked=false;  Log::Debug("App Mouse Hook Attach failed!: "+MouseHookHandle.ToString());}
			if (isHooked) Log::Debug("Attach App Keyboard Hook Success.");
		}
		return isHooked;
		//isHooked = true;
	}

	void KeyboardHook::DetachHook()
	{
		UnhookWindowsHookEx(KeyboardHookHandle);
		UnhookWindowsHookEx(MouseHookHandle);
		isHooked = false;
	}

	IntPtr KeyboardHook::OnKeyboardHook(int nCode, IntPtr wParam, IntPtr lParam)
	{
		if (nCode != HC_ACTION) return CallNextHookEx(KeyboardHookHandle, nCode, wParam, lParam);
		//GTA::Log::Debug("Keyboard hook: code: "+nCode+", w: "+wParam.ToInt32()+", l: "+lParam.ToInt32());
		Keys keys = safe_cast<Keys>(wParam.ToInt32());
		int infoFlags = lParam.ToInt32();
		bool isAltPressed = ((infoFlags & altPressedFlag) != 0);
		bool isGoingUp = ((infoFlags & beingReleasedFlag) != 0);
		if (isGoingUp)
		{
			GTA::Events::KeyUp::Invoke(nullptr,gcnew GTA::Events::KeyEventArgs(keys));
		}
		else
		{
			GTA::Events::KeyDown::Invoke(nullptr,gcnew GTA::Events::KeyEventArgs(keys));
		}
		return CallNextHookEx(KeyboardHookHandle, nCode, wParam, lParam);
	}

	IntPtr KeyboardHook::OnMouseHook(int nCode, IntPtr wParam, IntPtr lParam)
	{
		if (nCode != HC_ACTION) return CallNextHookEx(MouseHookHandle, nCode, wParam, lParam);
		System::Int32 button = wParam.ToInt32();
		if ((button <= 512) || (button > 526))return CallNextHookEx(MouseHookHandle, nCode, wParam, lParam); // non buttons
		//GTA::Log::Debug("Mouse hook: code: "+nCode+", w: "+button+", l: "+lParam.ToInt32());
		Keys keys;
		keys = Keys::None;
		bool down = false;
		if (button == WM_LBUTTONDOWN) {keys = Keys::LButton; down = true;}
		if (button == WM_LBUTTONUP) {keys = Keys::LButton;}
		if (button == WM_RBUTTONDOWN) {keys = Keys::RButton; down = true;}
		if (button == WM_RBUTTONUP) {keys = Keys::RButton;}
		if (button == WM_MBUTTONDOWN) {keys = Keys::MButton; down = true;}
		if (button == WM_MBUTTONUP) {keys = Keys::MButton;}

		if (!down)
		{
			GTA::Events::KeyUp::Invoke(nullptr,gcnew GTA::Events::KeyEventArgs(keys));
		}
		else
		{
			GTA::Events::KeyDown::Invoke(nullptr,gcnew GTA::Events::KeyEventArgs(keys));
		}
		return CallNextHookEx(MouseHookHandle, nCode, wParam, lParam);
	}

}